﻿/*
____
 L|ypps Web controls for ASP.NET

Copyright (c) 2009 Alessandro Zifiglio, http://www.Typps.com
EMail: alessandro@Typps.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Typps
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Data;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Reflection;
    using System.Resources;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Json;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading;
    using System.Web;
    using System.Web.Configuration;
    using System.Web.Handlers;
    using System.Web.Hosting;
    using System.Web.UI;
    using System.Web.UI.Design;
    using System.Web.UI.WebControls;

    using drawingAlias = System.Drawing;

    using imagingAlias = System.Drawing.Imaging;

    internal static class Helper
    {
        #region Methods

        public static string DecodeFrom64(string data)
        {
            byte[] decodedBytes = Convert.FromBase64String(data);
            return ASCIIEncoding.ASCII.GetString(decodedBytes);
        }

        public static string EncodeTo64(string data)
        {
            byte[] bytes = ASCIIEncoding.ASCII.GetBytes(data);
            return Convert.ToBase64String(bytes);
        }

        public static UrlPair ExtractQueryString(string url)
        {
            UrlPair p = new UrlPair();
            int start = url.IndexOf('?');
            if (start != -1)
            {
                p.Url = url.Substring(0, start);
                ++start;
                p.QueryString = url.Substring(start, url.Length - start);
            }
            else
            {
                p.Url = url;
            }
            return p;
        }

        public static string Find(string match, string[] items)
        {
            return Array.Find(items, delegate(string i)
            {
                return (i.IndexOf(match, StringComparison.OrdinalIgnoreCase) != -1);
            });
        }

        public static T FromJson<T>(string input)
        {
            if (string.IsNullOrEmpty(input)) return default(T);
            T returnValue = default(T);
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(input)))
            {
                returnValue = (T)ser.ReadObject(ms);
            }
            return returnValue;
        }

        public static string GetBackgroundImageUrl(ITheming component, string imgUrl)
        {
            return GetBackgroundImageUrl(component, imgUrl, false, false);
        }

        public static string GetBackgroundImageUrl(ITheming component, string imgUrl,
            bool forceRunTimeResourceUrl)
        {
            return GetBackgroundImageUrl(component, imgUrl, forceRunTimeResourceUrl, false);
        }

        public static string GetBackgroundImageUrl(ITheming component, string imgUrl,
            bool useRuntimeUrl, bool encode)
        {
            if (string.IsNullOrEmpty(imgUrl)) return string.Empty;
            string resourceUrl = string.Empty;
            string manifestName = Helper.GetManifestResourceName(imgUrl,
                component.ManifestResourceNamesCache);
            if (string.IsNullOrEmpty(manifestName) && imgUrl != string.Empty)
            {
                resourceUrl = (!component.IsDesignMode) ?
                    ((imgUrl.IndexOf('~') != -1) ?
                    VirtualPathUtility.ToAbsolute(imgUrl) : imgUrl) : imgUrl;
            }
            else if (!string.IsNullOrEmpty(manifestName))
            {
                resourceUrl = GetValidWebResourceUrl(component, manifestName,
                    useRuntimeUrl, encode);
            }
            return resourceUrl;
        }

        public static drawingAlias::Bitmap GetBitmapFromUrl(string url)
        {
            try
            {
                using (WebClient downloader = new WebClient())
                {
                    return new drawingAlias::Bitmap(downloader.OpenRead(url));
                }
            }
            catch (Exception exception)
            {
                throw exception;
            }
        }

        public static bool GetBooleanProperty(string propertyTag, DataRow dr)
        {
            object obj1 = dr[propertyTag];
            if ((obj1 != null) && !obj1.Equals(DBNull.Value))
            {
                return bool.Parse(obj1.ToString());
            }
            return false;
        }

        public static CultureInfo GetCurrentUICulture(out bool defaultLanguageKnown)
        {
            defaultLanguageKnown = true;
            string[] userLang = HttpContext.Current.Request.UserLanguages;
            if (userLang != null && userLang.Length > 0)
                return new CultureInfo(userLang[0]);

            defaultLanguageKnown = false;
            return Thread.CurrentThread.CurrentCulture;
        }

        public static string GetGlobalResourceObject(ResourceManager rm, string key)
        {
            return GetGlobalResourceObject(rm, key, null);
        }

        public static string GetGlobalResourceObject(ResourceManager rm, string key, CultureInfo ci)
        {
            if (ci == null)
                ci = Thread.CurrentThread.CurrentCulture;
            string value = rm.GetString(key, ci);
            if (string.IsNullOrEmpty(value))
                return string.Empty;
            else
                return value;
        }

        public static string GetManifestResourceName(string name,
            string[] manifestResourceNamesCache)
        {
            return GetManifestResourceName(name, manifestResourceNamesCache, false);
        }

        public static string GetManifestResourceName(string name,
            string[] manifestResourceNamesCache, bool isDebugMode)
        {
            if (string.IsNullOrEmpty(name)) return string.Empty;
            return Find(name, manifestResourceNamesCache);
        }

        public static string GetResourceBySkinContainer(string resourceName,
            string containerName, string[] manifestResourceNames)
        {
            return Array.Find(manifestResourceNames, delegate(string m)
            {
                return (m.IndexOf(containerName,
                    StringComparison.OrdinalIgnoreCase) != -1 && m.IndexOf(resourceName,
                    StringComparison.OrdinalIgnoreCase) != -1);
            });
        }

        public static Stream GetStreamFromUrl(string url, UriKind kind, HttpServerUtility server)
        {
            try
            {
                if (kind == UriKind.Relative)
                {
                    Stream fs = new FileStream(url, FileMode.Open, FileAccess.Read);
                    return fs;
                    //caller responsible for closing stream
                }
                else
                {
                    using (WebClient downloader = new WebClient())
                    {
                        return downloader.OpenRead(url);
                    }
                }
            }
            catch (Exception exception)
            {
                throw exception;//best to know what went wrong. you'll need to sniff it using fiddler
            }
        }

        public static string GetStringProperty(string propertyTag, DataRow dr)
        {
            object obj1 = dr[propertyTag];
            if ((obj1 != null) && !obj1.Equals(DBNull.Value))
            {
                return obj1.ToString();
            }
            return string.Empty;
        }

        public static string GetStyleRegistrationByScriptBlock(string css)
        {
            css = Helper.TrimWhiteSpace(css);
            string styleBlock =
            string.Format(@"var head = document.getElementsByTagName('head')[0];
            var css = '{0}';
            var style = document.createElement('style');
            style.setAttribute('type','text/css');
            if (style.styleSheet)
            {{
              style.styleSheet.cssText = css;
            }}
            else
            {{
              var styleTextNode = document.createTextNode(css);
              style.appendChild(styleTextNode);
            }}
            head.appendChild(style);", css.Replace("'", "\\'"));
            return css;
        }

        public static string GetTemplateData(string templateName, bool replaceID,
            Type t, string[] manifestResourceNamesCache,
            ResourceManager rm, string className, string clientId)
        {
            string resourcePath = Helper.GetManifestResourceName(templateName, manifestResourceNamesCache);
            string data = string.Empty;
            if (!string.IsNullOrEmpty(resourcePath))
            {
                using (StreamReader sr = new StreamReader(
                    t.Assembly.GetManifestResourceStream(resourcePath)))
                    data = sr.ReadToEnd();
            }
            else if (!string.IsNullOrEmpty(templateName))
            {
                using (StreamReader sr = new StreamReader(HostingEnvironment.MapPath(templateName)))
                    data = sr.ReadToEnd();
            }
            else
            {
                return string.Empty;
            }
            if (replaceID)
            {
                data = data.Replace("controlID", clientId);
                if (!string.IsNullOrEmpty(className))
                    data = data.Replace("$ID$", className);
            }
            ResourceSet rs = rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, true, true);
            foreach (DictionaryEntry dr in rs)
            {
                string key = (string)dr.Key;
                string value = (string)dr.Value;
                string match = string.Format("[{0}]", key);
                data = data.Replace(match, value);
            }
            return data;
        }

        public static string GetValidWebResourceUrl(ITheming component,
            string resourceName)
        {
            return GetValidWebResourceUrl(component, resourceName, false, true);
        }

        public static string GetValidWebResourceUrl(ITheming component,
            string resourceName, bool forceRunTimeResourceUrl, bool encoded)
        {
            if (component.ClientScript == null || resourceName == null) return null;
            if (!component.IsDesignMode || (component.IsDesignMode && forceRunTimeResourceUrl))
                return GetEmbeddedWebResourceUrl(component.ComponentType, resourceName, encoded);
            string url = component.ClientScript.GetWebResourceUrl(component.ComponentType, resourceName);
            if (encoded)
                url = url.Replace("&", "&amp;");

            return url;
        }

        public static string HashQueryString(string url)
        {
            UrlPair p = ExtractQueryString(url);
            string returnValue = p.Url;
            if (!string.IsNullOrEmpty(p.QueryString))
                returnValue += string.Format("?h={0}", EncodeTo64(p.QueryString));
            return returnValue;
        }

        public static bool HasUnitDefined(string unit)
        {
            int length = unit.Length;
            if (length <= 2) return false;

            string u = unit.Substring(unit.Length - 2, 2);
            switch (u)
            {
                case "px":
                case "pt":
                case "pc":
                case "in":
                case "mm":
                case "cm":
                case "em":
                case "ex":
                    return true;
            }
            return false;
        }

        public static bool HttpBrowserCapabilitiesIsUpLevel()
        {
            bool uplevel = false;
            HttpBrowserCapabilities browser = HttpContext.Current.Request.Browser;
            string name = browser.Browser.ToLower(CultureInfo.InvariantCulture);
            float version = (float)(browser.MajorVersion + browser.MinorVersion);
            switch (name)
            {
                case "ie":
                    uplevel = (version >= 6);
                    break;
                case "firefox":
                    uplevel = (version >= 2);
                    break;
                case "opera":
                    uplevel = (version >= 9);
                    break;
                case "safari":
                    uplevel = (version >= 3);
                    break;
                case "chrome":
                    uplevel = (version >= 1);
                    break;
            }
            return uplevel;
        }

        public static bool IsClientScriptBlockRegistered(string key, ScriptManager sm)
        {
            foreach (RegisteredScript rs in sm.GetRegisteredClientScriptBlocks())
            {
                if (rs.Key.Equals(key, StringComparison.OrdinalIgnoreCase))
                    return true;
            }
            return false;
        }

        public static bool IsUpLevel(bool useBrowserCaps)
        {
            if (useBrowserCaps)
            {
                return HttpBrowserCapabilitiesIsUpLevel();
            }
            else
            {
                UserAgent ua = new UserAgent();
                return ua.IsUpLevel();
            }
        }

        public static void RegisterSharedExternalStyleSheetReferences(Page p,
            string sharedCssQueryString)
        {
            RegisterSharedExternalStyleSheetReferences(p, sharedCssQueryString, null);
        }

        public static void RegisterSharedExternalStyleSheetReferences(Page p,
            string sharedCssQueryString, string treeviewCssQueryString)
        {
            string id = "Typps.Shared";
            string href = string.Empty;
            string url = Helper.GetEmbeddedWebResourceUrl(typeof(ResourceDummy), "css", true);
            string link = "<link type=\"text/css\" rel=\"Stylesheet\" href=\"{0}\" />";
            Control sharedCssStyle = p.Header.FindControl(id);

            if (sharedCssStyle == null)
            {
                href = p.ResolveUrl(string.Format("~/{0}{1}", url, sharedCssQueryString));
                Literal l1 = new Literal();
                l1.EnableViewState = false;
                l1.ID = id;
                l1.Text = string.Format(link, HashQueryString(href));
                p.Header.Controls.Add(l1);
            }

            if (string.IsNullOrEmpty(treeviewCssQueryString)) return;

            id = "Typps.Treeview";

            Control treeviewCssStyle = p.Header.FindControl(id);

            if (treeviewCssStyle == null)
            {
                href = p.ResolveUrl(string.Format("~/{0}{1}", url, treeviewCssQueryString));
                Literal l1 = new Literal();
                l1.EnableViewState = false;
                l1.ID = id;
                l1.Text = string.Format(link, HashQueryString(href));
                p.Header.Controls.Add(l1);
            }
        }

        /// <summary>
        /// This method has been deprecated. We now use RenderThumbnail directly in WebResourceHandler
        /// </summary>
        /// <param name="context"></param>
        /// <param name="outputStream"></param>
        /// <param name="url"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        public static void ResizeImageAndAppendToResponseOutputStream(HttpContext context, Stream outputStream, string url, int width, int height)
        {
            if (string.IsNullOrEmpty(url)) return;
            Uri uriToBitmap = new Uri(url, UriKind.RelativeOrAbsolute);
            Uri originalUri = context.Request.Url;
            if (!uriToBitmap.IsAbsoluteUri)
                url = string.Format("{0}{1}", originalUri.GetLeftPart(UriPartial.Authority), url);

            using (drawingAlias::Bitmap originalBitmap = GetBitmapFromUrl(url))
            {
                if (originalBitmap == null) return;

                imagingAlias::ImageCodecInfo[] codecInfoList = imagingAlias::ImageCodecInfo.GetImageEncoders();
                imagingAlias::ImageCodecInfo codecInfo = null;
                for (int num1 = 0; num1 < codecInfoList.Length; num1++)
                {
                    if ((codecInfoList[num1].FormatDescription.Equals("JPEG")))
                    {
                        codecInfo = codecInfoList[num1];
                        break;
                    }
                }
                imagingAlias::Encoder myEncoder = imagingAlias::Encoder.Quality;
                EncoderParameters myEncoderParameters = new EncoderParameters(1);
                myEncoderParameters.Param[0] = new EncoderParameter(myEncoder, 60L);

                if ((originalBitmap.Width > width || originalBitmap.Height > height))
                {
                    int proportionedHeight = int.Parse((originalBitmap.Height / (originalBitmap.Width / width)).ToString());
                    if (proportionedHeight > height || originalBitmap.Height < height)
                        proportionedHeight = height;
                    using (drawingAlias::Bitmap resizedBitmap = new drawingAlias::Bitmap(width, proportionedHeight))
                    {
                        Rectangle recDest = new Rectangle(0, 0, width, resizedBitmap.Height);
                        Graphics graphicCropper = Graphics.FromImage(resizedBitmap);
                        graphicCropper.SmoothingMode = SmoothingMode.HighQuality;
                        graphicCropper.CompositingQuality = CompositingQuality.HighQuality;
                        graphicCropper.InterpolationMode = InterpolationMode.High;
                        graphicCropper.DrawImage(originalBitmap, recDest, 0, 0, originalBitmap.Width, originalBitmap.Height, GraphicsUnit.Pixel);
                        resizedBitmap.Save(outputStream, codecInfo, myEncoderParameters);
                    }
                }
                else
                {
                    originalBitmap.Save(outputStream, codecInfo, myEncoderParameters);
                }
            }
        }

        public static string ToJsonString<T>(T input)
        {
            if (input == null) return null;
            string json = string.Empty;
            DataContractJsonSerializer ser = new DataContractJsonSerializer(
                typeof(T), new List<Type>(), int.MaxValue, true, new JsonSerializationSurrogate(), false);
            using (MemoryStream ms = new MemoryStream())
            {
                ser.WriteObject(ms, input);
                byte[] bytes = ms.ToArray();
                if (bytes.Length > 0)
                    json = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            }
            return json;
        }

        public static string TrimWhiteSpace(string text)
        {
            //Remove spaces and comments
            return Regex.Replace(text, @"\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/|\f|\n|\r|\t|\v|", string.Empty);
        }

        internal static string ConvertToPhysicalPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return path;

            string[] parts = path.Split('.');
            if (parts.Length < 3) return path;
            StringBuilder sb = new StringBuilder(parts.Length);
            for (int num1 = 0; num1 < parts.Length; num1++)
            {
                string p = parts[num1];
                sb.Append(p);
                if (num1 == parts.Length - 2)
                    sb.Append('.');
                else if (num1 <= parts.Length - 3)
                    sb.Append('/');
            }
            return string.IsNullOrEmpty(sb.ToString()) ? path : sb.ToString();
        }

        internal static string EnsurePhysicalPath(string path)
        {
            List<string> parts = new List<string>(path.Split('.'));
            int len = parts.Count;
            if (len > 0)
            {
                string fileExtention = '/' + parts[len - 2] + '.' + parts[len - 1];
                parts.RemoveAt(len - 1);
                parts.RemoveAt(len - 2);
                path = string.Join("/", parts.ToArray()) + fileExtention;
            }
            return path;
        }

        /// <summary>
        /// There are situations in which we want a WebResourceUrl and not one
        /// usable at designtime. The problem is when we are in a control designer
        /// this is determined for us by the default GetWebResourceUrl method,
        /// exposed by the ClientScriptManager. Currently not even reflection 
        /// can help us bypass this limitation ( i have invested quite a while here ),
        /// due to it's reliance on encryption.
        /// Having no choice, let's use our own custom handler. This is much better,
        /// more flexible to us and is less lengthy url wise
        /// </summary>
        /// <param name="t"></param>
        /// <param name="resourceName"></param>
        /// <param name="htmlEncoded"></param>
        /// <returns></returns>
        internal static string GetEmbeddedWebResourceUrl(Type t, string resourceName, bool encoded)
        {
            resourceName = resourceName.Replace(t.Namespace + '.', "");
            long lastWrite = System.IO.File.GetLastWriteTime(new Uri(t.Assembly.CodeBase).LocalPath).Ticks;
            return FormatEmbeddedWebResourceUrl(resourceName, lastWrite, encoded);
        }

        internal static string GetPhysicalPathByResourceNamespace(string path, ITheming component)
        {
            if (path.IndexOf('/') != -1) return path;
            bool isFormatString = path.IndexOf('{') != -1;
            string skinName = string.Format(".{0}.", component.Skin.ToString());
            string replacementSkinName = string.Format(".{0}.", component.SelectedSkin);
            path = path.Replace(skinName, replacementSkinName).Replace("Typps.Resources.Skins.", component.CustomSkinFolder);
            List<string> parts = new List<string>(path.Split('.'));
            int len = parts.Count;
            if (len > 0)
            {
                string fileExtention = string.Empty;
                if (!isFormatString)
                {
                    fileExtention = '/' + parts[len - 2] + '.' + parts[len - 1];
                    parts.RemoveAt(len - 1);
                    parts.RemoveAt(len - 2);
                }
                path = string.Join("/", parts.ToArray()) + fileExtention;
            }
            return path;
        }

        internal static string MapPath(IDesignerHost service, string path, PathType p)
        {
            if (path.Length != 0)
            {
                if (IsAbsolutePhysicalPath(path))
                {
                    return path;
                }
                WebFormsRootDesigner designer = null;
                if ((service != null) && (service.RootComponent != null))
                {
                    designer = service.GetDesigner(service.RootComponent) as WebFormsRootDesigner;
                    if (designer != null)
                    {
                        string appRelativeUrl = designer.ResolveUrl(path);
                        IWebApplication application = (IWebApplication)service.GetService(typeof(IWebApplication));
                        if (application != null)
                        {
                            IProjectItem projectItemFromUrl = application.GetProjectItemFromUrl(appRelativeUrl);
                            if (projectItemFromUrl != null)
                            {
                                if (p == PathType.Physical)
                                    return projectItemFromUrl.PhysicalPath;
                                else
                                    return projectItemFromUrl.AppRelativeUrl;
                            }
                        }
                    }
                }
            }
            return null;
        }

        private static string FormatEmbeddedWebResourceUrl(string resourceName, long lastWrite, bool encoded)
        {
            if (encoded)
            {
                return string.Format("typps.axd?d={0}&amp;t={1}", resourceName, lastWrite);
            }
            return string.Format("typps.axd?d={0}&t={1}", resourceName, lastWrite);
        }

        private static bool IsAbsolutePhysicalPath(string path)
        {
            if ((path == null) || (path.Length < 3))
            {
                return false;
            }
            return (path.StartsWith(@"\\", StringComparison.Ordinal) || ((char.IsLetter(path[0]) && (path[1] == ':')) && (path[2] == '\\')));
        }

        #endregion Methods
    }

    internal class JsonSerializationSurrogate : IDataContractSurrogate
    {
        #region Methods

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            return null;
        }

        public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
        {
            return null;
        }

        public Type GetDataContractType(Type type)
        {
            return type;
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
            return obj;
        }

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            if (obj is Unit)
                return ((Unit)(obj)).ToString();
            else if (obj is TransitionName)
                return obj.ToString();
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            return null;
        }

        public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
        {
            return typeDeclaration;
        }

        #endregion Methods

        #region Other

        /*just change the way the pixel class is serialized to json. we want it in value+px format*/

        #endregion Other
    }
}